using System;
using System.Collections.Generic;
using System.Text;

namespace CommonTypes
{
    public class Tranformations3D
    {



            
        /// <summary>
        /// Calculate the normal of a shape
        /// </summary>
        /// <param name="x">shape X[] points</param>
        /// <param name="y">shape Y[] points</param>
        /// <param name="z">shape Z[] points</param>
        /// <returns>normals (x,y,z)</returns>
        public static double[] calcNormal(double[] x, double[] y, double[] z)
        {
            double V1x = x[0] - x[1];
            double V1y = y[0] - y[1];
            double V1z = z[0] - z[1];

            double V2x = x[1] - x[2];
            double V2y = y[1] - y[2];
            double V2z = z[1] - z[2];


            double[] res = new double[3];

            res[0] = (V1y * V2z) - (V1z * V2y);
            res[1] = (V1z * V2x) - (V1x * V2z);
            res[2] = (V1x * V2y) - (V1y * V2x);

            ReduceNormalToInit(ref res[0], ref res[1], ref res[2]);

            return res;

        }


        /// <summary>
        /// Reduces normals to unit length
        /// </summary>
        /// <param name="x">normal x</param>
        /// <param name="y">normal y</param>
        /// <param name="z">normal z</param>
        public static void ReduceNormalToInit(ref double x, ref double y, ref double z)
        {
            double length = System.Math.Sqrt(x * x + y * y + z * z);

            if (length == 0.0)
                length = 1.0;

            x /= length;
            y /= length;
            z /= length;
        }


        /// <summary>
        /// Get the point (x,y) lies on a circle with radius r , center xc,yc, having angle a 
        /// </summary>
        /// <param name="xc">Center x of circle</param>
        /// <param name="yc">Center y of circle</param>
        /// <param name="r">Circle radius</param>
        /// <param name="a">angle</param>
        /// <param name="x">points x</param>
        /// <param name="y">points y</param>
        public static void E_CirclePoint(double xc, double yc, double r, double a, out double x, out double y)
        {
            x = xc + r * System.Math.Cos(a * System.Math.PI / 180.0);
            y = yc + r * System.Math.Sin(a * System.Math.PI / 180.0);
        }


        /// <summary>
        /// Get the angles per X,Y axis of a segment 
        /// </summary>
        /// <param name="x1">segment start x</param>
        /// <param name="y1">segment start y</param>
        /// <param name="z1">segment start z</param>
        /// <param name="x2">segment end x</param>
        /// <param name="y2">segment end y</param>
        /// <param name="z2">segment end z</param>
        /// <param name="AngleX">Angle per X axis</param>
        /// <param name="AngleY">Angle per Y axis</param>
        public static void ExtractAngles(double x1, double y1, double z1, double x2, double y2, double z2, ref double AngleX, ref double AngleY)
        {
            AngleY = GetRotationAnglePerYAxis(x1, y1, z1, x2, y2, z2);
            AngleX = GetRotationAnglePerXAxis(x1, y1, z1, x2, y2, z2);
        }


        /// <summary>
        /// Get the rotation angle per Y axis
        /// </summary>
        /// <param name="x1">segment start x</param>
        /// <param name="y1">segment start y</param>
        /// <param name="z1">segment start z</param>
        /// <param name="x2">segment end x</param>
        /// <param name="y2">segment end y</param>
        /// <param name="z2">segment end z</param>
        /// <returns>angle of rotation per Y axis</returns>
        public static double GetRotationAnglePerYAxis(double x1, double y1, double z1, double x2, double y2, double z2)
        {
            if (z1 == z2)
            {
                if (x1 > x2)
                    return -90;
                else
                    return 90;
            }
            else if (x1 == x2)
            {
                if (z1 > z2)
                    return 180;
                else
                    return 0;
            }
            else
            {
                double[] x = { x1, x2, x2 };
                double[] y = { y1, y2, y1 + y2 + 100 };
                double[] z = { z1, z2, z2 };

                double[] normal = Tranformations3D.calcNormal(x, y, z);

                double a = System.Math.Atan(normal[2] / normal[0]);

                double angle;

                if (x1 <= x2 && z2 <= z1)
                    angle = 180 - Geometry.RadToDeg(a);
                else if (x2 <= x1 && z1 <= z2)
                    angle = -Geometry.RadToDeg(a);
                else if (x1 <= x2 && z1 <= z2)
                    angle = -Geometry.RadToDeg(a);
                else if (x2 <= x1 && z2 <= z1)
                    angle = 180 - Geometry.RadToDeg(a);
                else
                    angle = 0;

                return angle;
            }
        }


        /// <summary>
        /// Get the rotation angle per X axis
        /// </summary>
        /// <param name="x1">segment start x</param>
        /// <param name="y1">segment start y</param>
        /// <param name="z1">segment start z</param>
        /// <param name="x2">segment end x</param>
        /// <param name="y2">segment end y</param>
        /// <param name="z2">segment end z</param>
        /// <returns>angle of rotation per X axis</returns>
        public static double GetRotationAnglePerXAxis(double x1, double y1, double z1, double x2, double y2, double z2)
        {
            if (y1 == y2)
                return 0;
            else if (z1 == z2 && x1 == x2)
            {
                if (y1 > y2)
                    return 90;
                else
                    return -90;
            }
            else
            {
                double aa = System.Math.Sqrt((x2 - x1) * (x2 - x1) + (z2 - z1) * (z2 - z1));
                double a = System.Math.Atan((y2 - y1) / aa);

                double angle;


                if (y2 > y1)
                {
                    if (x2 >= x1 && z2 >= z1)
                        angle = 180 - Geometry.RadToDeg(a);// ok
                    else if (x1 >= x2 && z1 >= z2)
                        angle = 180 - Geometry.RadToDeg(a); // ok
                    else if (x2 >= x1 && z1 >= z2)
                        angle = -Geometry.RadToDeg(a); // ok
                    else if (x1 >= x2 && z2 >= z1)
                        angle = -Geometry.RadToDeg(a); // ok
                    else
                        angle = 90;
                }
                else
                {
                    if (x2 >= x1 && z2 >= z1)
                        angle = -Geometry.RadToDeg(a);// ok
                    else if (x1 >= x2 && z1 >= z2)
                        angle = 180 - Geometry.RadToDeg(a); // ok 
                    else if (x2 >= x1 && z1 >= z2)
                        angle = -Geometry.RadToDeg(a); // ok 
                    else if (x1 >= x2 && z2 >= z1)
                        angle = -Geometry.RadToDeg(a); // ok
                    else
                        angle = 90;
                }


                return angle;
            }
        }


        /// <summary>
        /// GetRotationMatrix a point around X axis
        /// </summary>
        /// <param name="x">points x</param>
        /// <param name="y">points y</param>
        /// <param name="z">points z</param>
        /// <param name="angle">angle of rotation</param>
        public static void RotatePerXAxis(ref  double x, ref double y, ref double z, double angle)
        {
            double angleX = Geometry.DegToRad(angle);

            double y1 = y * System.Math.Cos(angleX) - z * System.Math.Sin(angleX);
            double z1 = y * System.Math.Sin(angleX) + z * System.Math.Cos(angleX);

            y = y1;
            z = z1;
        }


        /// <summary>
        /// GetRotationMatrix a point around Y axis
        /// </summary>
        /// <param name="x">points x</param>
        /// <param name="y">points y</param>
        /// <param name="z">points z</param>
        /// <param name="angle">angle of rotation</param>
        public static void RotatePerYAxis(ref  double x, ref double y, ref double z, double angle)
        {
            double angleY = Geometry.DegToRad(angle);

            double x1 = x * System.Math.Cos(angleY) + z * System.Math.Sin(angleY);
            double z1 = -x * System.Math.Sin(angleY) + z * System.Math.Cos(angleY);

            x = x1;
            z = z1;

        }


        /// <summary>
        /// GetRotationMatrix a point around Z axis 
        /// </summary>
        /// <param name="x">points x</param>
        /// <param name="y">points y</param>
        /// <param name="z">points z</param>
        /// <param name="angle">angle of rotation</param>
        public static void RotatePerZAxis(ref  double x, ref double y, ref double z, double angle)
        {
            double angleZ = Geometry.DegToRad(angle);

            double x1 = x * System.Math.Cos(angleZ) - y * System.Math.Sin(angleZ);
            double y1 = x * System.Math.Sin(angleZ) + y * System.Math.Cos(angleZ);

            x = x1;
            y = y1;

        }

    }


}
